using System;
using Slaks.VectorBasics.Containers;

namespace Slaks.VectorBasics.Algorithms
{
	public class VectorDistances
	{
		public static double GetCosinus(Vector one, Vector two)
		{
			double result = 0.0;

			VectorItemFrequencyList oneFreqList, twoFreqList;

			GetVectorsSameLength(one, two, out oneFreqList, out twoFreqList);

			if (oneFreqList.Count != twoFreqList.Count)
				throw new VectorException(String.Format("VectorDistances.GetCosinus : oneFreqList size {0} differs from twoFreqList size {1}", oneFreqList.Count, twoFreqList.Count));

			double dPower2 = 0.0;
			double wPower2 = 0.0;
			double wd = 0.0;
			for (int i = 0; i < oneFreqList.Count; i++)
			{
				wd += oneFreqList[i]*twoFreqList[i];
				dPower2 += oneFreqList[i]*oneFreqList[i];
				wPower2 += twoFreqList[i]*twoFreqList[i];
			}

			if (wd == 0.0) return 1;
			result = 1 - wd/Math.Sqrt(dPower2*wPower2);

			return result;
		}

		public static double GetJacaard(Vector one, Vector two)
		{
			double result = 0.0;


			VectorItemFrequencyList oneFreqList, twoFreqList;

			GetVectorsSameLength(one, two, out oneFreqList, out twoFreqList);

			if (oneFreqList.Count != twoFreqList.Count)
				throw new VectorException(String.Format("VectorDistances.GetJacaard : oneFreqList size {0} differs from twoFreqList size {1}", oneFreqList.Count, twoFreqList.Count));

			double xy = 0.0;
			double xPower2 = 0.0;
			double yPower2 = 0.0;

			for (int i = 0; i < oneFreqList.Count; i++)
			{
				xy += oneFreqList[i]*twoFreqList[i];
				xPower2 += oneFreqList[i]*oneFreqList[i];
				yPower2 += twoFreqList[i]*twoFreqList[i];
			}

			if (xy == 0.0) return 1;
			result = 1 - xy/(xPower2 + yPower2 - xy);

			return result;
		}

		public static double GetWordnet(Vector one, Vector two)
		{
			double commonDistance = 0;
			one.SortVectorItems();
			two.SortVectorItems();

			VectorItemList oneList = one.GetVectorItemList();
			VectorItemList twoList = two.GetVectorItemList();

			int sizeOne = oneList.Count;
			int sizeTwo = twoList.Count;

			int i = 0;
			int j = 0;

			int compare;
			int count = 0;
			while (i < oneList.Count && j < twoList.Count)
			{
				compare = oneList[i].CompareTo(twoList[j]);
				if (compare < 0)
				{
					i++;
				}
				else if (compare == 0)
				{
					commonDistance += oneList[i].Frequency*twoList[j].Frequency;
					i++;
					j++;
					++count;
				}
				else
				{
					j++;
				}
			} //until i and j less then size of the vector

			return 1 - (commonDistance/(commonDistance + sizeOne + sizeTwo - count - count));
		}

		private static void GetVectorsSameLength(Vector oneVector, Vector twoVector, out VectorItemFrequencyList oneFreqList, out VectorItemFrequencyList twoFreqList)
		{
			oneVector.SortVectorItems();
			twoVector.SortVectorItems();

			oneFreqList = new VectorItemFrequencyList();
			twoFreqList = new VectorItemFrequencyList();

			VectorItemList oneList = oneVector.GetVectorItemList();
			VectorItemList twoList = twoVector.GetVectorItemList();

			int i = 0;
			int j = 0;

			int compare;

			while (i < oneList.Count && j < twoList.Count)
			{
				compare = oneList[i].CompareTo(twoList[j]);
				if (compare < 0)
				{
					oneFreqList.Add(oneList[i].Frequency);
					twoFreqList.Add(0.0);
					i++;
				}
				else if (compare == 0)
				{
					oneFreqList.Add(oneList[i].Frequency);
					twoFreqList.Add(twoList[j].Frequency);
					i++;
					j++;
				}
				else
				{
					oneFreqList.Add(0.0);
					twoFreqList.Add(twoList[j].Frequency);
					j++;
				}
			} //until i and j less then size of the vector

			//now add the rest frequencies from the vector that is larger

			if (i < oneList.Count) //oneList is greater
			{
				for (int m = i; m < oneList.Count; m++)
				{
					oneFreqList.Add(oneList[m].Frequency);
					twoFreqList.Add(0.0);
				}
			}
			else if (j < twoList.Count) //twoList is greater
			{
				for (int m = j; m < twoList.Count; m++)
				{
					twoFreqList.Add(twoList[m].Frequency);
					oneFreqList.Add(0.0);
				}
			}

		}
	}
}